﻿using System;

namespace YuYuKiCms.Framework.Extention
{
    public static class DateTimeExtensions
    {
        public static int ToUnixTimestamp(this DateTime dateTime)
        {
            return
                (int)
                    Math.Floor(dateTime.Subtract(new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Local)).TotalSeconds);
        }

        public static int ToUtcUnixTimestamp(this DateTime dateTime)
        {
            return
                (int)
                    Math.Floor(
                        dateTime.ToUniversalTime()
                            .Subtract(new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc))
                            .TotalSeconds);
        }

        public static DateTime AddBusinessDays(this DateTime date, int days)
        {
            if (days < 0)
            {
                throw new ArgumentException("The arguments days cannot be less than zero.", "days");
            }
            if (days == 0) return date;
            if (date.DayOfWeek == DayOfWeek.Saturday)
            {
                date = date.AddDays(2);
                days -= 1;
            }
            else if (date.DayOfWeek == DayOfWeek.Sunday)
            {
                date = date.AddDays(1);
                days -= 1;
            }

            date = date.AddDays(days/5*7);
            var extraDays = days%5;

            if ((int) date.DayOfWeek + extraDays > 5)
            {
                extraDays += 2;
            }
            return date.AddDays(extraDays);
        }

        public static int GetBusinessDaysTo(this DateTime start, DateTime end)
        {
            if (start.DayOfWeek == DayOfWeek.Saturday) start = start.AddDays(2);
            else if (start.DayOfWeek == DayOfWeek.Sunday) start = start.AddDays(1);

            if (end.DayOfWeek == DayOfWeek.Saturday) end = end.AddDays(-1);
            else if (end.DayOfWeek == DayOfWeek.Sunday) end = end.AddDays(-2);

            var diff = (int) end.Subtract(start).TotalDays;
            var result = diff/7*5 + diff%7;

            if (end.DayOfWeek < start.DayOfWeek) result -= 2;
            return result;
        }

        /// <summary>
        ///     Renvoie le prochain jour ouvrable y compris le jour courant
        /// </summary>
        /// <param name="date"></param>
        /// <returns></returns>
        public static DateTime GetNextBusinessDay(this DateTime date)
        {
            // Comme dimanche = 0 … samedi = 6
            // ((DayOfWeek - 6) % 6) + 6 → lundi = 0, mardi = 1, … dimanche = 5
            var dayOfWeek = (((int) date.DayOfWeek - 7)%7) + 5;

            // Controle si c'est un weekend
            if (dayOfWeek >= 4)
                // on ajoute 2 si on est samedi
                // on ajoute 1 si on est dimanche
                // dimanche = 0 … samedi = 6
                // On cherche à avoir dimanche = 1 … samedi = 2 pour ajouter 1 ou 2 jours
                date = date.AddDays(((int) date.DayOfWeek%5) + 1);

            return date;
        }

        /// <summary>
        ///     Calcul l'age via une date donnée en paramètre
        /// </summary>
        /// <param name="date"></param>
        /// <returns></returns>
        public static int CalculAge(this DateTime date)
        {
            return date.CalculAge(DateTime.Now);
        }

        /// <summary>
        ///     Calcul l'age via une date donnée en paramètre
        /// </summary>
        /// <param name="date"></param>
        /// <param name="dateFrom"></param>
        /// <returns></returns>
        public static int CalculAge(this DateTime date, DateTime dateFrom)
        {
            var age = dateFrom.Year - date.Year;

            // Correction si la date est déjà passée ou pas
            if (date > dateFrom.AddYears(-age)) age--;

            return age;
        }

        /// <summary>
        ///     Retourne le trimestre pour une date donnée
        /// </summary>
        /// <param name="date"></param>
        /// <returns></returns>
        public static int Trimestre(this DateTime date)
        {
            return (date.Month - 1)/3 + 1;
        }

        public static DateTime GetDateFromTrimestre(this DateTime date, int annee, int trimestre)
        {
            return new DateTime(annee, (trimestre*3) - 2, 1);
        }

        public static DateTime PremierJourduTrimestre(this DateTime date)
        {
            return new DateTime(date.Year, (date.Trimestre()*3) - 2, 1);
        }

        public static DateTime DernierJourduTrimestre(this DateTime date)
        {
            return date.PremierJourduTrimestre().AddMonths(3).AddDays(-1);
        }

        public static DateTime Max(this DateTime date, DateTime date2)
        {
            return (date >= date2) ? date : date2;
        }

        public static DateTime Min(this DateTime date, DateTime date2)
        {
            return (date <= date2) ? date : date2;
        }

        public static int NombreJourAnneeCivile(this DateTime date)
        {
            return new DateTime(date.Year, 1, 1).AddYears(1).AddDays(-1).DayOfYear;
        }
    }
}